#include"front/lexical.h"

#include<map>
#include<cassert>
#include<string>

#define TODO assert(0 && "todo")

// #define DEBUG_DFA
// #define DEBUG_SCANNER

std::string frontend::toString(State s) {
    switch (s) {
    case State::Empty: return "Empty";
    case State::Ident: return "Ident";
    case State::IntLiteral: return "IntLiteral";
    case State::FloatLiteral: return "FloatLiteral";
    case State::op: return "op";
    default:
        assert(0 && "invalid State");
    }
    return "";
}

std::set<std::string> frontend::keywords= {
    "const", "int", "float", "if", "else", "while", "continue", "break", "return", "void"
};

frontend::DFA::DFA(): cur_state(frontend::State::Empty), cur_str() {}

frontend::DFA::~DFA() {}

bool frontend::DFA::next(char input, Token& buf) {
#ifdef DEBUG_DFA
#include<iostream>
    std::cout << "in state [" << toString(cur_state) << "], input = \'" << input << "\', str = " << cur_str << "\t";
#endif
    bool ret = false;
    switch (cur_state) {
    case State::Empty:
        if (isspace(input)) {
            cur_state = State::Empty;
        } else if (isalpha(input) || input == '_') {
            cur_state = State::Ident;
            cur_str += input;
        } else if (isdigit(input)) {
            cur_state = State::IntLiteral;
            cur_str += input;
        } else if (ispunct(input) && input != '_') {
            cur_state = State::op;
            cur_str += input;
        } else {
            assert(0 && "invalid character!");
        }
        break;
    case State::Ident:
        if (isalnum(input) || input == '_') {
            cur_state = State::Ident;
            cur_str += input;
        } else {
            buf.type = typeof_keyword(cur_str);
            buf.type = buf.type == TokenType::NONE ? TokenType::IDENFR : buf.type;
            buf.value = cur_str;
            ret = true;
            reset();
            next(input, buf);
        }
        break;
    case State::IntLiteral:
        if (isalnum(input)) {
            cur_state = State::IntLiteral;
            cur_str += input;
        } else if (input == '.') {
            cur_state = State::FloatLiteral;
            cur_str += input;
        } else {
            buf.type = TokenType::INTLTR;
            buf.value = cur_str;
            ret = true;
            reset();
            next(input, buf);
        }
        break;
    case State::FloatLiteral:
        if (isalnum(input)) {
            cur_state = State::FloatLiteral;
            cur_str += input;
        } else {
            buf.type = TokenType::FLOATLTR;
            buf.value = cur_str;
            ret = true;
            reset();
            next(input, buf);
        }
        break;
    case State::op:
        if (ispunct(input) && input != '_') {
            if (typeof_op(cur_str + input) == TokenType::NONE) { // 单字符运算符，例如 + - * /
                buf.type = typeof_op(cur_str);
                assert(buf.type != TokenType::NONE);
                buf.value = cur_str;
                ret = true;
                reset();
                next(input, buf);
            } else { // 双字符运算符，例如 >= <= && ||
                cur_str += input;
                buf.type = typeof_op(cur_str);
                buf.value = cur_str;
                ret = true;
                reset();
            }
        } else {
            buf.type = typeof_op(cur_str);
            assert(buf.type != TokenType::NONE);
            buf.value = cur_str;
            ret = true;
            reset();
            next(input, buf);
        }
        break;
    }
#ifdef DEBUG_DFA
    std::cout << "next state is [" << toString(cur_state) << "], next str = " << cur_str << "\t, ret = " << ret << std::endl;
#endif
    return ret;
}

void frontend::DFA::reset() {
    cur_state = State::Empty;
    cur_str = "";
}

frontend::Scanner::Scanner(std::string filename): fin(filename) {
    if(!fin.is_open()) {
        assert(0 && "in Scanner constructor, input file cannot open");
    }
}

frontend::Scanner::~Scanner() {
    fin.close();
}

std::string frontend::Scanner::	preproccess(std::ifstream &ifs) {
    std::string ret = "";
    enum {
        EMPTY,      // 初态
        DIV,        // /
        DIVDIV_,    // // ...
        DIVMUL_,    // /* ...
        DIVMUL_MUL,// /* ... *
    } state = EMPTY;
    char c;
    while ((c = ifs.get()) != EOF) {
        switch (state) {
            case EMPTY:
                if (c == '/') {
                    state = DIV;
                } else {
                    ret += c;
                }
                break;
            case DIV:
                if (c == '/') {
                    state = DIVDIV_;
                } else if (c == '*') {
                    state = DIVMUL_;
                } else {
                    state = EMPTY;
                    ret += '/';
                    ret += c;
                }
                break;
            case DIVDIV_:
                if (c == '\r' || c == '\n') {
                    state = EMPTY;
                }
                break;
            case DIVMUL_:
                if (c == '*') {
                    state = DIVMUL_MUL;
                }
                break;
            case DIVMUL_MUL:
                if (c == '/') {
                    state = EMPTY;
                } else {
                    state = DIVMUL_;
                }
                break;
        }
    }
    return ret;
}

std::vector<frontend::Token> frontend::Scanner::run() {
    std::vector<Token> ret;
    Token tk;
    DFA dfa;
    std::string s = preproccess(fin) + '\n';
    for (char c: s) {
        if (dfa.next(c, tk)) {
#ifdef DEBUG_SCANNER
#include<iostream>
            std::cout << "token: " << toString(tk.type) << "\t" << tk.value << std::endl;
#endif
            ret.push_back(tk);
        }
    }
    return ret;
}